import asyncio
from mcp import ClientSession
from contextlib import AsyncExitStack
from openai import OpenAI
from dotenv import load_dotenv
from typing import Optional
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
import os
import json
class McpCLient:
    def __init__(self):
        """初始化 MCP 客户端"""
        self.exit_stack = AsyncExitStack()
        self.openai_key = os.getenv("LLM_API_KEY")
        self.base_url = os.getenv("LLM_BASE_URL")
        self.model = os.getenv("LLM_MODEL")
        if not self.openai_key:
            raise ValueError("❌ 未找到 LLM API Key，请在 .env 文件中设置 OPENAI_API_KEY")
        self.client = OpenAI(api_key=self.openai_key, base_url=self.base_url)
        self.session:Optional[ClientSession] = None

        print(f"当前使用模型：{self.model}")

    async def connect_to_server(self,server_script_path:str):
        """连接到 MCP 服务器"""
        is_python = server_script_path.endswith(".py")
        is_js = server_script_path.endswith(".js")
        if not is_python and not is_js:
            raise ValueError("❌ 服务器脚本路径必须以 .py 或 .js 结尾")
        command = "python" if is_python else "node"
        server_params = StdioServerParameters(
            command=command,
            args=[server_script_path],
            env=None
        )
        # 启动 MCP 服务器并建立连接
        stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params))
        self.stdio,self.writ = stdio_transport
        self.session = await self.exit_stack.enter_async_context(ClientSession(self.stdio, self.writ))
        await self.session.initialize()

        # 列出可使用的 MCP 服务器工具
        responses = await self.session.list_tools()
        tools = responses.tools
        print(f"\n✅ MCP 客户端已初始化，已连接到服务器，可使用的工具：")
        for tool in tools:
            print(f"- {tool.name}: {tool.description}")
    async def process_query(self, query: str):
        """
        使用大模型处理查询并调用可用的 MCP 工具
        """
        messages = [{'role': 'user', 'content': query}]
        response = await self.session.list_tools()

        # 可用的工具
        available_tools = [
            {
                "type":"Function",
                "function":{
                    "name":tool.name,
                    "description":tool.description,
                    "input_schema":tool.inputSchema,
                }
            }for tool in response.tools] 
        
        response = self.client.chat.completions.create(
            model=self.model,
            messages=messages,
            tools=available_tools,
        )
        # 处理返回的数据
        content = response.choices[0]
        if content.finish_reason == "stop":
            print(f"\n🤖 [LLM Response] LLM：{content.message.content}")
            return
        while content.finish_reason == "tool_calls":
            # 如果需要调用工具 则处理调用
            tool_call = content.message.tool_calls[0]
            tool_name = tool_call.function.name
            tool_args = json.loads(tool_call.function.arguments)
            # 执行工具
            tool_result = await self.session.call_tool(tool_name,tool_args)
            print(f"工具结果:{tool_result}")
            print(f"\n🤖 [Function Call] 工具：{tool_name}，参数：{tool_args}")
            # 将模型需要的工具和工具返回的结果存入messages
            messages.append(content.message)
            messages.append({
                "role": "tool",
                "content": tool_result.content[0].text,
                "tool_call_id": tool_call.id,
            })
            # 调用LLM查看是否还需要工具
            response = self.client.chat.completions.create(
                model=self.model,
                messages=messages,
                tools=available_tools,
            )
            content = response.choices[0]
            if content.finish_reason == "stop":
                print(f"\n🤖 [LLM Response] LLM：{content.message.content}")
                return
        
    def printContent(self,response):
        full_content = ""
        for chunk in response:
            if chunk.choices and chunk.choices[0].delta.content:
                # 一字一字地输出
                for char in chunk.choices[0].delta.content:
                    print(char, end='', flush=True)
                    full_content += char
        print()  # 在最后添加一个换行符
    async def chat_loop(self):
        """运行交互式聊天循环"""
        print("\nMCP 客户端已启动！输入 'quit' 退出")
        while True:
            try:
                query = input("\nQuery：").strip()
                if query.lower() == "quit":
                    break
                proquery = await self.process_query(query)
                
            except Exception as e:
                print(f"\n⚠️发生错误：{str(e)}")

    async def clearup(self):
        """清理资源"""
        await self.exit_stack.aclose()
        print("\nMCP 客户端已关闭！")


async def main():
    if len(sys.argv) < 2:
        print("Usage: python client.py <path_to_server_script>")
        sys.exit(1)
    client = McpCLient()
    try:
        await client.connect_to_server(sys.argv[1])
        await client.chat_loop()
    finally:
        await client.clearup()

if __name__ == "__main__":
    import sys
    asyncio.run(main())